package com.winway.jxl.core;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import com.winway.jxl.anomotion.CellColum;
import com.winway.jxl.anomotion.CellEntity;
import com.winway.jxl.util.ClassMenager;

import jxl.Workbook;
import jxl.format.Alignment;
import jxl.format.Colour;
import jxl.format.Orientation;
import jxl.format.UnderlineStyle;
import jxl.format.VerticalAlignment;
import jxl.write.Label;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import jxl.write.biff.RowsExceededException;

/**
 * Excel写入工具
 * @author mr-lao
 *
 * @param <T>
 */
public class ExcelWriter<T> {
	private Class<?> cls;
	private WritableWorkbook mWwb;
	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

	public void setDateFormat(String pattern) {
		sdf = new SimpleDateFormat(pattern);
	}

	public void setDateFormat(SimpleDateFormat sdf) {
		this.sdf = sdf;
	}

	/**
	 * 
	 * @param filePath
	 *            保存数据的文件路径，如果文件不存在，则主动创建文件
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public ExcelWriter(String filePath) throws FileNotFoundException, IOException {
		this(new File(filePath));
	}

	/**
	 * 
	 * @param file
	 *            保存数据的文件，如果文件不存在，则主动创建文件
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public ExcelWriter(File file) throws FileNotFoundException, IOException {
		deelFile(file);
		mWwb = Workbook.createWorkbook(file);
		// this(new FileOutputStream(file));
	}

	public ExcelWriter(OutputStream os) throws IOException {
		mWwb = Workbook.createWorkbook(os);
	}

	// 如果文件不存在，则创建文件
	private static void deelFile(File file) throws IOException {
		if (!file.exists()) {
			if (file.getParentFile() != null && !file.getParentFile().exists()) {
				file.getParentFile().mkdirs();
			}
			file.createNewFile();
		}
	}

	private boolean hadClosed = false;

	/**
	 * 关闭
	 * 
	 * @throws WriteException
	 * @throws IOException
	 */
	public void close() throws WriteException, IOException {
		if (hadClosed) {
			return;
		}
		mWwb.close();
		hadClosed = true;
	}

	public void resetInit(String filePath) throws IOException {
		resetInit(new File(filePath));
	}

	public void resetInit(File file) throws IOException {
		deelFile(file);
		resetInit(new FileOutputStream(file));
	}

	public void resetInit(OutputStream os) throws IOException {
		mWwb = Workbook.createWorkbook(os);
		hadClosed = false;
	}

	public void writeExcel(List<T> list) throws ClassNotFoundException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException, RowsExceededException, WriteException, IOException {
		writeExcel(list, "sheet1");
	}

	public void writeExcel(List<T> list, String sheetName) throws ClassNotFoundException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException, RowsExceededException, WriteException, IOException {
		writeExcel(list, sheetName, 0);
	}

	public void writeExcel(List<T> list, String sheetName, int sheetIndex)
			throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
			RowsExceededException, WriteException, IOException {
		writeExcel(list, sheetName, sheetIndex, true);
	}

	/**
	 * 写入Excel文件（此方法只有生成文件单元格）
	 * 
	 * @param list
	 * @param sheetName
	 * @param sheetIndex
	 * @param needHeaders
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws RowsExceededException
	 * @throws WriteException
	 * @throws IOException
	 */
	public void writeExcel(List<T> list, String sheetName, int sheetIndex, boolean needHeaders)
			throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
			RowsExceededException, WriteException, IOException {
		cls = list.get(0).getClass();
		WritableSheet sheet = mWwb.createSheet(sheetName, sheetIndex);
		int start = 0;
		// 写入表头，如果有的话
		if (needHeaders) {
			List<Label> lineLaels = createHeaders(cls, 0);
			for (Label l : lineLaels) {
				sheet.addCell(l);
			}
			start = 1;
		}
		for (int i = 0; i < list.size(); i++) {
			List<Label> lineLaels = createLineLaels(list.get(i), i + start, 0, sdf);
			for (Label l : lineLaels) {
				sheet.addCell(l);
			}
		}
		setSheetStyle(sheet, cls);
		mWwb.write();
		close();
	}

	/**
	 * 设置表格格式
	 * @param sheet
	 * @param exlEntityClass
	 * @throws ClassNotFoundException
	 * @throws RowsExceededException 
	 */
	private void setSheetStyle(WritableSheet sheet, Class<?> exlEntityClass)
			throws ClassNotFoundException, RowsExceededException {
		HashMap<String, List<Annotation>> annotations = ClassMenager.getAnnotations(exlEntityClass,
				ClassMenager.MODE_FIELD, CellColum.class.getName());
		Set<String> keySet = annotations.keySet();
		for (String key : keySet) {
			CellColum cell = (CellColum) annotations.get(key).get(0);
			sheet.setColumnView(cell.index(), cell.lenth());
		}
		for (int i = 0; i < sheet.getRows(); i++) {
			sheet.setRowView(i, 360);
		}
	}

	/**
	 * 生成表头Label
	 * 
	 * @param cls
	 * @return
	 * @throws ClassNotFoundException
	 * @throws WriteException
	 */
	static List<Label> createHeaders(Class<?> cls, int offset) throws ClassNotFoundException, WriteException {
		ArrayList<Label> list = new ArrayList<Label>();
		parseCellColumHeaders(cls, list, offset);
		parseCellEntityHeaders(cls, list);
		return list;
	}

	/**
	 * 创建表头
	 * 
	 * @param ann
	 * @return
	 * @throws WriteException
	 */
	static Label createHeaderLabel(CellColum ann, int offset) throws WriteException {
		int colum = ann.index() + offset;
		WritableCellFormat cellFormat = new WritableCellFormat();
		cellFormat.setOrientation(Orientation.HORIZONTAL);
		cellFormat.setWrap(ann.wrap());
		cellFormat.setAlignment(Alignment.CENTRE);
		cellFormat.setVerticalAlignment(VerticalAlignment.CENTRE);
		WritableFont font = new WritableFont(WritableFont.TIMES, 10, WritableFont.BOLD, false,
				UnderlineStyle.NO_UNDERLINE, Colour.BLACK);
		cellFormat.setFont(font);
		return new Label(colum, 0, ann.headerName(), cellFormat);
	}

	/**
	 * 解释Colum字段生成表头
	 * 
	 * @param cls
	 * @param list
	 * @throws ClassNotFoundException
	 * @throws WriteException
	 */
	static void parseCellColumHeaders(Class<?> cls, List<Label> list, int offset)
			throws ClassNotFoundException, WriteException {
		HashMap<String, List<Annotation>> annotations = ClassMenager.getAnnotations(cls, ClassMenager.MODE_FIELD,
				CellColum.class.getName());
		if (annotations == null || annotations.size() == 0) {
			return;
		}
		Set<String> keySet = annotations.keySet();
		for (String fieldName : keySet) {
			CellColum ann = (CellColum) annotations.get(fieldName).get(0);
			int colum = ann.index();
			if (colum < 0) {
				continue;
			}
			list.add(createHeaderLabel(ann, offset));
		}
	}

	/**
	 * 解释Entity字段生成表头
	 * 
	 * @param cls
	 * @param list
	 * @throws ClassNotFoundException
	 * @throws WriteException
	 */
	static void parseCellEntityHeaders(Class<?> cls, List<Label> list) throws ClassNotFoundException, WriteException {
		HashMap<String, List<Annotation>> annotations = ClassMenager.getAnnotations(cls, ClassMenager.MODE_FIELD,
				CellEntity.class.getName());
		if (annotations == null || annotations.size() == 0) {
			return;
		}
		Set<String> keySet = annotations.keySet();
		for (String fieldName : keySet) {
			CellEntity ann = (CellEntity) annotations.get(fieldName).get(0);
			if (ann.excel() == true) {
				Field field = ClassMenager.getField(cls, fieldName);
				List<Label> headers = createHeaders(field.getType(), ann.offset());
				copyList(list, headers);
			}
		}
	}

	static void copyList(List<Label> desc, List<Label> source) {
		for (Label label : source) {
			desc.add(label);
		}
	}

	/**
	 * 根据对象生成Label数组
	 * 
	 * @param obj
	 *            数据对象
	 * @param row
	 *            行号
	 * @return
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws WriteException
	 */
	public static <T> List<Label> createLineLaels(T obj, int row, int offset, SimpleDateFormat sdf)
			throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
			WriteException {
		ArrayList<Label> list = new ArrayList<Label>();
		creatCellColumLabels(list, obj, row, offset, sdf);
		creatCellEntityLabels(list, obj, row, sdf);
		return list;
	}

	/**
	 * 生成Entity对应的Label
	 * 
	 * @param list
	 * @param obj
	 * @param row
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws WriteException
	 */
	static <T> void creatCellEntityLabels(List<Label> list, T obj, int row, SimpleDateFormat sdf)
			throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
			WriteException {
		Class<? extends Object> cls = obj.getClass();
		HashMap<String, List<Annotation>> annotations = ClassMenager.getAnnotations(cls, ClassMenager.MODE_FIELD,
				CellEntity.class.getName());
		if (annotations == null || annotations.size() == 0) {
			return;
		}
		Set<String> keySet = annotations.keySet();
		for (String fieldName : keySet) {
			CellEntity ann = (CellEntity) annotations.get(fieldName).get(0);
			if (ann.excel()) {
				Object invoke = ClassMenager.getGetMethod(cls, fieldName).invoke(obj);
				if (null == invoke) {
					continue;
				}
				List<Label> lineLaels = createLineLaels(invoke, row, ann.offset(), sdf);
				copyList(list, lineLaels);
			}
		}
	}

	/**
	 * 生成Colum对应的Label
	 * 
	 * @param list
	 * @param obj
	 * @param row
	 * @throws ClassNotFoundException
	 * @throws WriteException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 */
	static <T> void creatCellColumLabels(List<Label> list, T obj, int row, int offset, SimpleDateFormat sdf)
			throws ClassNotFoundException, WriteException, IllegalAccessException, IllegalArgumentException,
			InvocationTargetException {
		Class<? extends Object> cls = obj.getClass();
		HashMap<String, List<Annotation>> annotations = ClassMenager.getAnnotations(cls, ClassMenager.MODE_FIELD,
				CellColum.class.getName());
		if (annotations == null || annotations.size() == 0) {
			return;
		}
		Set<String> keySet = annotations.keySet();
		for (String fieldName : keySet) {
			CellColum ann = (CellColum) annotations.get(fieldName).get(0);
			int colum = ann.index() + offset;
			if (colum < 0) {
				continue;
			}
			Object value = null;
			try {
				value = ClassMenager.getGetMethod(cls, fieldName).invoke(obj);
			} catch (Exception e) {
				value = ClassMenager.getIsMethod(cls, fieldName).invoke(obj);
			}
			if (value instanceof List) {
				List<?> valueList = (List<?>) value;
				for (int i = 0; i < valueList.size(); i++) {
					list.add(createLabel(valueList.get(i), colum + i, row, sdf, ann.wrap()));
				}
			} else {
				list.add(createLabel(value, colum, row, sdf, ann.wrap()));
			}
		}
	}

	static Label createLabel(Object value, int colum, int row, SimpleDateFormat sdf, boolean wrap)
			throws WriteException {
		Label l = new Label(colum, row, value == null ? "" : value + "");
		if ((value instanceof Date) && sdf != null) {
			l.setString(sdf.format((Date) value));
		}
		WritableCellFormat cellFormat = new WritableCellFormat();
		cellFormat.setWrap(wrap);
		cellFormat.setAlignment(Alignment.CENTRE);
		cellFormat.setVerticalAlignment(VerticalAlignment.CENTRE);
		WritableFont font = new WritableFont(WritableFont.ARIAL, 9, WritableFont.NO_BOLD, false,
				UnderlineStyle.NO_UNDERLINE, Colour.BLACK);
		cellFormat.setFont(font);
		l.setCellFormat(cellFormat);
		return l;
	}

}
